// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.

// Adapted from the Direct3D bump mapping sample
// http://code.msdn.microsoft.com/windowsapps/Direct3D-bump-mapping-619d8d23/

#pragma once
#include "BasicTimer.h"
#include "Common\StepTimer.h"

#define LONLAT_GAIN    0.003f // sensitivity adjustment for mouse/touch
#define LONLATKEY_GAIN 0.04f  // sensitivity adjustment for keys

// A basic Longitude/Latitude Controller class for navigation on a sphere
// Uses touch, WASD keyboard, or mouse pointer movement
ref class LonLatController
{
internal:
    static property LonLatController^ Current
    {
        LonLatController^ get()
        {
            assert(IsMainThread());
            if (m_current == nullptr)
            {
                m_current = ref new LonLatController();
            }
            return m_current;
        }
    }

    // Suspends processing of input.
    void SuspendInput();

    // Resumes processing of input.
    void ResumeInput();

    // Begins sphere animation to the provided latitude and longitude.
    concurrency::task<void> AnimateToLatLonAsync(float latitude, float longitude);

    // Cancels any active sphere animation.
    void CancelLatLonAnimation(bool moveToEnd);

    // accessor to set position of controller
    void SetPosition(_In_ DirectX::XMFLOAT3 pos);

    // accessor to set position of controller
    void SetOrientation(_In_ float latitude, _In_ float longitude);

    // returns the position of the controller object
    DirectX::XMFLOAT3 get_Position();

    // returns the point at which the camera controller is facing
    DirectX::XMFLOAT3 get_LookPoint();

    // Updates the orientation of the sphere based on user input and animations.
    void Update();

    // methods to save and restore state
    void SaveInternalState(_In_ Windows::Foundation::Collections::IMap<Platform::String^, Platform::Object^>^ state);
    void LoadInternalState(_In_ Windows::Foundation::Collections::IMap<Platform::String^, Platform::Object^>^ state);

private:
    LonLatController();

    void ProcessRotateCommand(const DirectX::XMFLOAT2& rotationDelta);

    // Methods to get input from the UI pointers
    void OnPointerPressed(
        _In_ Windows::UI::Core::CoreWindow^ sender,
        _In_ Windows::UI::Core::PointerEventArgs^ args
        );

    void OnPointerMoved(
        _In_ Windows::UI::Core::CoreWindow^ sender,
        _In_ Windows::UI::Core::PointerEventArgs^ args
        );

    void OnPointerReleased(
        _In_ Windows::UI::Core::CoreWindow^ sender,
        _In_ Windows::UI::Core::PointerEventArgs^ args
        );

    void OnPointerWheelChanged(
        _In_ Windows::UI::Core::CoreWindow^ sender,
        _In_ Windows::UI::Core::PointerEventArgs^ args
        );

    void OnPointerCaptureLost(
        _In_ Windows::UI::Core::CoreWindow^ sender,
        _In_ Windows::UI::Core::PointerEventArgs^ args
        );

    void OnManipulationStarted(
        _In_ Windows::UI::Input::GestureRecognizer^ sender,
        _In_ Windows::UI::Input::ManipulationStartedEventArgs^ args);

    void OnManipulationUpdated(
        _In_ Windows::UI::Input::GestureRecognizer^ sender,
        _In_ Windows::UI::Input::ManipulationUpdatedEventArgs^ args);

    void OnManipulationCompleted(
        _In_ Windows::UI::Input::GestureRecognizer^ sender,
        _In_ Windows::UI::Input::ManipulationCompletedEventArgs^ args);

    void OnTapped(
        _In_ Windows::UI::Input::GestureRecognizer^ sender,
        _In_ Windows::UI::Input::TappedEventArgs^ args);

    static LonLatController^ m_current;

    // properties of the controller object
    float m_radius;      // Distance from center of sphere
    float m_longitude;   // Left-right coordinate on the sphere
    float m_latitude;    // Pole-to-pole coordinate on the sphere

    // properties of the LonLat control
    bool m_lonLatInUse; // the look control is in use
    uint32 m_lonLatPointerID1; // id of the first pointer down
    uint32 m_lonLatPointerID2; // id of the second pointer down
    DirectX::XMFLOAT2 m_lonLatLastPoint1; // last point (from last frame)
    DirectX::XMFLOAT2 m_lonLatLastPoint2; // last point (from last frame)
    uint32 m_lonLatPointerGesture;

    DirectX::XMFLOAT3 m_lonLatCommand; // the net command from the move control
    std::map<uint32, DirectX::XMFLOAT2> m_pointersDown;
    float  m_lastDistance;

    // Cached reference to the Window.
    Platform::Agile<Windows::UI::Core::CoreWindow> m_window;

    // For sphere acceleration
    Platform::Agile<Windows::UI::Input::GestureRecognizer^> m_gestureRecognizer;

    // Registration tokens for input events.
    Windows::Foundation::EventRegistrationToken m_tokenManipulationStarted;
    Windows::Foundation::EventRegistrationToken m_tokenManipulationUpdated;
    Windows::Foundation::EventRegistrationToken m_tokenManipulationCompleted;
    Windows::Foundation::EventRegistrationToken m_tokenTapped;
    Windows::Foundation::EventRegistrationToken m_tokenPointerPressed;
    Windows::Foundation::EventRegistrationToken m_tokenPointerMoved;
    Windows::Foundation::EventRegistrationToken m_tokenPointerReleased;
    Windows::Foundation::EventRegistrationToken m_tokenPointerWheelChanged;

    // For animating zoom.
    Windows::Foundation::Point m_pointerVelocity;
    BasicTimer^ m_velocityTimer;

    // For animating zoom.
    BasicTimer^ m_zoomAnimationTimer;
    bool m_animateZoom;
    float m_zoomInitial;
    float m_zoomTarget;

    // For input suspension.
    int32_t m_inputSuspendCount;

    // For sphere animation.
    concurrency::task_completion_event<void> m_animationEvent;
    BasicTimer^ m_animationTimer;
    float m_rotationMaxTime;
    float m_rotationTimeRemaining;
    DirectX::XMFLOAT4 m_startRotation;  // a quaternion
    DirectX::XMFLOAT4 m_endRotation;    // a quaternion
};
